package frame.common;

import java.io.Serializable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

import frame.utils.FrameUtils;

/**
 * 基本bean,共其他bean来继承。提供一个通用的tostring方法
 * 提供一个logger对象，共记录日志使用  ? 为什么要记录日志呢，
 * 
 * 表示一个java对象是一个表在内存中的映射。可以直接操作该对象进行数据库的曾删改查操作。
 * 该类不能直接使用，故为抽象类。
 * 而且一次只能操作一条记录。类似批量插入，批量删除，批量更新都不支持。
 * @author MR.CUIPENG
 *
 */
public class BaseBean implements Serializable{
	
	private static final long serialVersionUID = 4986063587196387105L;
	
	protected String tableName;//表示具体对象对应的表名
	//TODO 测试这里是否会有并发问题
	protected List<Object> args=new LinkedList<Object>();
	private  Logger logger = Logger.getLogger(getClass());
	
	/**
	 * 返回该对象的一个插入语句
	 * 插入语句的处理相对来说比较简单，直接将字段及字段的值排列在语句中即可。
	 * @return
	 */
	public final  String insert(String[] orders){
		StringBuilder strb=new StringBuilder(64);
		strb.append("insert into ").append(this.tableName);
		return strb.toString();
	}
	/**
	 * 需要被子类覆盖,默认返回一个恒等式。
	 * 用于拼接非等于条件的sql语句。比如 between and , in
	 * @return
	 */
	protected String appender(){
		return "1=1";
	}

	/**
	 * 用于返回appender语句对应的preparement参数,
	 * 子类在重载appender方法的时候向父类的变量args中
	 * 添加对应的preparement值，该方法不允许子类覆盖。
	 * @return
	 */
	public final Object[] args(){
		return args.size()>0?args.toArray():null;
	}
	/**
	 * 返回该对象的一个查询语句,如果某个字段值为空则不会被加入到过滤条件中
	 * 这里只支持简单的并列关心，如果要支持复杂的查询逻辑可以在外置sql文件中处理，也可以
	 * 在子类中覆盖这个方法，这个方法是比较有用的。
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public   String select(){
		Field[] fields = this.getClass().getDeclaredFields();
		StringBuilder strb=new StringBuilder(64);
		boolean where=false;
		strb.append("select * from ").append(this.tableName);
		
		if(!where){// append where
			strb.append(" where ").append(appender());
			where=true;
		}
		
		/**
		 * Shield Java Access Control
		 */
		AccessibleObject.setAccessible(fields, true);
		try {
			for (Field f : fields) {
					Object val = f.get(this);
					Class c=f.getType();
					if(!StringUtils.isEmpty(val)&&!Modifier.isStatic(f.getModifiers())){
						
						if(c.equals(String.class)){
							strb.append(" and ").append(f.getName()).append(" like '%").append(val.toString()).append("%'");
						}else if(c.equals(int.class)||c.equals(Integer.class)||
								 c.equals(double.class)||c.equals(Double.class)||
								 c.equals(long.class)||c.equals(Long.class)||
								 c.equals(short.class)||c.equals(Short.class)||
								 c.equals(BigDecimal.class)||c.equals(BigInteger.class)){
							strb.append(" and ").append(f.getName()).append("=").append(val.toString());
						}
						
					}
			}
		} catch (Exception e) {
			logger.error("反射对象属性时发生异常", e);
		}
		return strb.toString();
	}
	/**
	 * 返回该对象的一个更新语句
	 * 更新语句的处理最为复杂，如果是单条记录的话，肯定是以主键来索引了。但是其他字段为
	 * 空的时候是更新数据库表对应字段为空能?还是忽略掉该字段呢？
	 * 更新的关键依据是是否发生变化。如果要判断该记录是否发生变化，则需要保存之前的记录，以便
	 * 与当前入库记录进行对比。
	 * @return
	 */
	public final  String update(){
		return "update";
	}
	/**
	 * 返回该对象的一个删除语句
	 * 需要根据主键来生成语句，主键可以是被注解了的字段，也可以是父类中的一个字段。
	 * @return
	 */
	public final  String delete(){
		return "delete";
	}
	
	@Override
	public  String toString() {
		return FrameUtils.toString(this);
	}
	
}
